﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MessageWorkflow.Handler;
using MessageWorkflow.Dispatch;

namespace MessageWorkflow
{
    public static class WorkflowChannelExtensions
    {
        /// <summary>
        /// Subscribes to messages of type T with the action specified as its handler
        /// </summary>
        public static void Subscribe<T>(this WorkflowChannel channel, Action<T> handler)
        {
            if (channel == null)
                throw new ArgumentNullException("channel");

            channel.Subscribe(new WorkflowHandlerConfiguration(WorkflowHandlerFactory.FromLambda(handler)));
        }

        /// <summary>
        /// Subscribes to messages of type T with the handler specified
        /// </summary>
        public static void Subscribe(this WorkflowChannel channel, IWorkflowHandler handler)
        {
            if (channel == null)
                throw new ArgumentNullException("channel");

            channel.Subscribe(new WorkflowHandlerConfiguration(handler));
        }

        /// <summary>
        /// Dispatches the message synchronously to the channel and throws on the first ex found
        /// </summary>
        public static T Dispatch<T>(this WorkflowChannel channel,
            T message)
        {
            if (channel == null)
                throw new ArgumentNullException("channel");

            IErrorStrategy errorStrategy = new SingleErrorStrategy();

            return channel.Dispatch(message, errorStrategy);
        }

        /// <summary>
        /// Dispatches the message to the channel and if any exceptions occour, this will also be dispatched to the channel
        /// </summary>
        public static void DispatchAsync<T>(this WorkflowChannel channel,
            T message)
        {
            if (channel == null)
                throw new ArgumentNullException("channel");

            channel.DispatchAsync(message, null);
        }


        /// <summary>
        /// Dispatches the message specified to the channel specified and calls the onComplete once completed 
        /// If any exceptions occour during the execution, this will be dispatched in the same channel
        /// </summary>
        public static void DispatchAsync<T>(this WorkflowChannel channel,
            T message,
            Action<T> onComplete)
        {
            if (channel == null)
                throw new ArgumentNullException("channel");

            DispatchErrorStrategy errorStrategy = new DispatchErrorStrategy(channel);

            channel.BeginDispatch(state =>
            {
                IAsyncResult asyncState = (IAsyncResult)state;

                channel.EndDispatch(asyncState);

                if (onComplete != null)
                    onComplete(message);

            }, message, errorStrategy);
        }

        /// <summary>
        /// Dispatches the message specified to the channel specified and calls the onSuccess and onError code respectively
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exceptions are handeled by the onError callback specified")]
        public static void DispatchAsync<T>(this WorkflowChannel channel,
            T message,
            Action<T> onSuccess,
            Action<Exception> onError)
        {
            IErrorStrategy errorStrategy = new SingleErrorStrategy();

            channel.BeginDispatch(state =>
            {
                IAsyncResult asyncState = (IAsyncResult)state;

                try
                {
                    channel.EndDispatch(asyncState);

                    if (onSuccess != null)
                    {
                        onSuccess(message);
                    }
                }
                catch (Exception ex)
                {
                    if (onError != null)
                    {
                        onError(ex);
                    }
                }
            }, message, errorStrategy);
        }

    }
}
